# -*-coding: Utf-8 -*-
# @File : day03 .py
# author: Chimengmeng
# blog_url : https://www.cnblogs.com/dream-ze/
# Time：2023/9/29
from fastapi import APIRouter, status, Form, File, UploadFile, HTTPException
from pydantic import BaseModel, EmailStr
from typing import Optional, Union, List

app04 = APIRouter()


### 响应模型

# 定义基本类
class UserBase(BaseModel):
    # 定义字段 username ： 用户名 类型为 str ： 字符串
    username: str
    # 定义字段 email ： 邮箱 类型为 EmailStr ： 自动校验邮箱
    email: EmailStr
    # 定义字段 mobile ： 手机号 类型为 str ： 字符串
    mobile: str = "10086"
    # 定义字段 full_name ： 手机号 类型为 Optional[str] ： 可选填参数 ，字符串类型
    full_name: Optional[str] = None


# 定义用户登录类
class UserIn(UserBase):
    # 登陆需要校验密码
    # 定义字段 password ： 密码 类型为 str ： 字符串
    password: str


# 定义用户响应信息类
class UserOut(UserBase):
    # 返回信息 不需要将用户的密码作为响应数据返回
    ...


class UserOutTwo(UserBase):
    address: Optional[str] = "My address"


# 新建两个用户
users = {
    "user01": {"username": "user01", "password": "123123", "email": "user01@example.com"},
    "user02": {"username": "user02", "password": "123456", "email": "user02@example.com", "mobile": "110"}
}


# response_model : 默认响应数据模型
# response_model_exclude_unset ： 只使用前端传过来的值，而不使用默认值（mobile: str = "10086" ---> 不使用10086 而是使用前端传入的数据/函数中赋值）
@app04.post('/response_model', response_model=UserOut, response_model_exclude_unset=True)
async def response_model(user: UserIn):
    """response_model_exclude_unset=True表示默认值不包含在响应中，仅包含实际给的值，如果实际给的值与默认值相同也会包含在响应中"""
    # password不会被返回
    print(user.password)
    return users["user01"]


# 响应字段取两个模型类的并集
@app04.post(
    '/response_model/attributes',
    # 只使用固定的响应模型类
    # response_model=UserOut,
    # 取 两个模型类的并集
    # response_model=Union[UserIn, UserOut],
    # 当 response_model 为 列表类型时，可以使用多个响应模型类
    response_model=List[UserOut],
    # # 返回的相应数据中 必须包含的字段
    # response_model_include=["username", "email"],
    # # 返回的响应数据中必须排除的字段
    # response_model_exclude=["mobile"]
)
async def response_model_attributes(user: UserIn):
    return [user, user]


#### 响应状态码
@app04.post('/status_code', status_code=status.HTTP_200_OK)
async def status_attribute():
    return {"status_code": 200, "status_type": str(type(status.HTTP_200_OK))}


### 表单数据处理
@app04.post("/login/")
async def login(
        # username 用户名  str 字符串类型 必填 通过表单验证 下同
        username: str = Form(...), password: str = Form(...)):  # 定义表单参数
    """用Form类需要pip install python-multipart; Form类的元数据和校验方法类似Body/Query/Path/Cookie"""
    return {"username": username}


"""Request Files 单文件、多文件上传及参数详解"""


@app04.post("/file")
async def file_(file: bytes = File(...)):  # 如果要上传多个文件 files: List[bytes] = File(...)
    """使用File类 文件内容会以bytes的形式读入内存 适合于上传小文件"""
    return {"file_size": len(file)}


@app04.post("/upload_files")
async def upload_files(files: List[UploadFile] = File(...)):  # 如果要上传单个文件 file: UploadFile = File(...)
    """
    使用UploadFile类的优势:
    1.文件存储在内存中，使用的内存达到阈值后，将被保存在磁盘中
    2.适合于图片、视频大文件
    3.可以获取上传的文件的元数据，如文件名，创建时间等
    4.有文件对象的异步接口
    5.上传的文件是Python文件对象，可以使用write(), read(), seek(), close()操作
    """
    for file in files:
        contents = await file.read()
        print(contents)
    return {"filename": files[0].filename, "content_type": files[0].content_type}


"""【见run.py】FastAPI项目的静态文件配置"""

"""Path Operation Configuration 路径操作配置"""


@app04.post(
    "/path_operation_configuration",
    response_model=UserOut,
    # tags=["Path", "Operation", "Configuration"],
    summary="This is summary",
    description="This is description",
    response_description="This is response description",
    deprecated=True,
    status_code=status.HTTP_200_OK
)
async def path_operation_configuration(user: UserIn):
    """
    Path Operation Configuration 路径操作配置
    :param user: 用户信息
    :return: 返回结果
    """
    return user.dict()


"""Handling Errors 错误处理"""


@app04.get("/http_exception")
async def http_exception(city: str):
    if city != "Beijing":
        raise HTTPException(
            # 状态码
            status_code=404,
            # 错误的详细信息
            detail="City not found!",
            # 响应的 响应头
            headers={"X-Error": "Error"})
    return {"city": city}


@app04.get("/http_exception/{city_id}")
async def override_http_exception(city_id: int):
    if city_id == 1:
        raise HTTPException(
            # 同上
            status_code=418,
            detail="Nope! I don't like 1.")
    return {"city_id": city_id}
